using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Microblaze.Simulator.Engine;

namespace Weazel.Microblaze.Simulator.Components.Microblaze.Instructions
{
    public class Addi : Instruction
    {
        public enum AddiInstructionKind
        {
            None,     // Error
            addikc,   // add with carry, keep carry
            addic,    // add with carry
            addik,    // add keep carry
            addi,      // add immediate
        }

        private AddiInstructionKind kind;

        public override int CycleCount
        {
            get { return 1; }
        }

        public Addi()
            : base("addi")
        {
        }

        public override void Set(uint address, uint value)
        {
            base.Set(address, value);

            this.kind =
              (Opcode & 0x0E) == 0x0E ? AddiInstructionKind.addikc :
              (Opcode & 0x0E) == 0x0A ? AddiInstructionKind.addic :
              (Opcode & 0x0E) == 0x0C ? AddiInstructionKind.addik :
              (Opcode & 0x0E) == 0x08 ? AddiInstructionKind.addi :
               AddiInstructionKind.None;

            if (kind == AddiInstructionKind.None)
                throw new UnknownInstructionException(value);
        }

        public override void Execute(MicroblazeComponent MicroblazeComponent)
        {
            bool C =
              kind == AddiInstructionKind.addic ||
              kind == AddiInstructionKind.addikc;

            bool K =
              kind == AddiInstructionKind.addik ||
              kind == AddiInstructionKind.addikc;

            uint sa = MicroblazeComponent.ReadRegister(SourceRegisterA);
            uint carry = MicroblazeComponent.ArithmeticCarry ? (uint)1 : (uint)0;
            ulong result = 0;

            if (!C)
                result = (ulong)sa + (ulong)GetImmediateValue(MicroblazeComponent);
            else
                result = (ulong)sa + (ulong)GetImmediateValue(MicroblazeComponent) + (ulong)carry;

            MicroblazeComponent.WriteRegister(DestinationRegister, (uint)result, Address);

            if (!K)
            {
                MicroblazeComponent.ArithmeticCarry = (result >> 32) != 0 ? true : false;
            }
        }

        public override string ToString()
        {
            return string.Format("{0} r{1}, r{2}, {3}",
              kind,
              DestinationRegister,
              SourceRegisterA,
              ImmediateValue
            );
        }
    }
}
